/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package webapi;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.inspur.edp.lcm.metadata.api.IMdExtRuleContent;
import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.lcm.metadata.api.entity.Metadata4RefDto;
import com.inspur.edp.lcm.metadata.api.entity.MetadataDto;
import com.inspur.edp.lcm.metadata.api.entity.MetadataIndexItemDto;
import com.inspur.edp.lcm.metadata.api.entity.MetadataProject;
import com.inspur.edp.lcm.metadata.api.entity.MetadataType;
import com.inspur.edp.lcm.metadata.api.entity.Page;
import com.inspur.edp.lcm.metadata.api.entity.ProcessMode;
import com.inspur.edp.lcm.metadata.api.entity.metadataindex.MetadataIndexDto;
import com.inspur.edp.lcm.metadata.api.service.MetadataProjectService;
import com.inspur.edp.lcm.metadata.api.service.MetadataService;
import com.inspur.edp.lcm.metadata.api.service.PackageGenerateService;
import com.inspur.edp.lcm.metadata.api.service.RefCommonService;
import com.inspur.edp.lcm.metadata.common.MetadataDtoConverter;
import com.inspur.edp.lcm.metadata.common.Utils;
import com.inspur.edp.lcm.metadata.common.configuration.MdExtRuleSerializerHelper;
import com.inspur.edp.lcm.metadata.devcommon.ManagerUtils;
import com.inspur.edp.lcm.metadata.spi.MdExtendRuleSerializer;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;

public class MdServiceWebApi {
    private MetadataService metadataService;

    private MetadataService getMetadataService() {
        if (metadataService == null) {
            metadataService = SpringBeanUtils.getBean(MetadataService.class);
        }
        return metadataService;
    }

    private MetadataProjectService metadataProjectService;

    private MetadataProjectService getMetadataProjectService() {
        if (metadataProjectService == null) {
            metadataProjectService = SpringBeanUtils.getBean(MetadataProjectService.class);
        }
        return metadataProjectService;
    }

    private RefCommonService refCommonService;

    private RefCommonService getRefCommonService() {
        if (refCommonService == null) {
            refCommonService = SpringBeanUtils.getBean(RefCommonService.class);
        }
        return refCommonService;
    }

    PackageGenerateService packService;

    private PackageGenerateService getPackService() {
        if (packService == null) {
            packService = SpringBeanUtils.getBean(PackageGenerateService.class);
        }
        return packService;
    }

    /**
     * @param spacePath        元数据路径
     * @param metadataTypeList 元数据类型
     * @return java.util.List<com.inspur.edp.lcm.metadata.api.entity.MetadataDto>
     * @author zhongchq
     **/
    @GET
    public List<MetadataDto> getMetadataList(@QueryParam(value = "path") String spacePath,
        @QueryParam(value = "metadataTypeList") String metadataTypeList) {
        List<GspMetadata> result = getMetadataService().getMetadataList(spacePath, handleMetadataTypeList(metadataTypeList));

        String projPath = getMetadataProjectService().getProjPath(spacePath);
        ProcessMode processMode = ProcessMode.GENERATION;
        if (projPath != null && !projPath.isEmpty()) {
            processMode = getMetadataProjectService().getProcessMode(projPath);
        }
        List<MetadataDto> metadataDtos = asMdDtos(result, processMode);
        metadataDtos.forEach(dto -> dto.setRelativePath(ManagerUtils.getRalativePath(dto.getRelativePath())));
        return metadataDtos;
    }

    /**
     * 根据当前工程路径获取bo下的元数据列表（会根据processMode过滤）
     *
     * @param path
     * @param metadataTypeList
     * @return List
     */
    @GET
    @Path(value = "/metadataListInBo")
    public List<MetadataDto> getMetadataListInBo(@QueryParam(value = "path") String path,
        @QueryParam(value = "metadataTypeList") String metadataTypeList) {
        List<MetadataDto> result = getMetadataService().getMetadataListInBo(path, handleMetadataTypeList(metadataTypeList));
        result.forEach(dto -> dto.setRelativePath(ManagerUtils.getRalativePath(dto.getRelativePath())));
        return result;
    }

    /***
     * @author zhongchq
     * @param metadataPath
     * 元数据所在路径信息，当前工程下的某个路径，相对路径
     * @param metadataID
     *元数据唯一标识
     * @return com.inspur.edp.lcm.metadata.api.entity.MetadataDto
     **/
    @GET
    @Path(value = "/relied")
    public MetadataDto getRefMetadata(@QueryParam(value = "metadataPath") String metadataPath,
        @QueryParam(value = "metadataID") String metadataID) {
        Utils.setMetadataPath(metadataPath);
        return MetadataDtoConverter.asDto(getRefCommonService().getRefMetadata(metadataID));
    }

    /**
     * 获取各元数据配置的元数据类型信息及后缀信息
     *
     * @return java.util.List<com.inspur.edp.lcm.metadata.api.entity.MetadataType>
     * @author zhongchq
     **/
    @GET
    @Path(value = "/typeList")
    public List<MetadataType> getMetadataTypeList() {
        return getMetadataService().getMetadataTypeList();
    }

    /**
     * @param projectPath 获取工程信息
     * @return com.inspur.edp.lcm.metadata.api.entity.MetadataProject
     * @author zhongchq
     **/
    @GET
    @Path(value = "/projectInfo")
    public MetadataProject getMetadataProjInfo(@QueryParam(value = "projectPath") String projectPath) {
        if (projectPath == null || projectPath.isEmpty()) {
            throw new IllegalArgumentException("projectPath参数不能为空");
        }
        return getMetadataService().getMetadataProjInfo(projectPath);
    }

    /**
     * @param scopeType             查找范围0.默认 1.本地 2.已安装 3.已引用 4.仓库中
     * @param text                  查询内容
     * @param path                  相对于开发根路径的路径信息
     * @param metadataTypeList      可以传递想要获取的元数据类型，类型用逗号隔开，如metadataTypeList=.be,.cmp,.api 该参数可以不赋值，默认获取所有类型
     * @param conflictAvoid         冲突避免
     * @param selectedPackageSource 包源
     * @param page                  是否分页
     * @param pageSize              每页大小
     * @param pageIndex             第几页
     * @return com.inspur.edp.lcm.metadata.api.entity.metadataindex.MetadataIndexDto
     * @author zhongchq
     **/
    @GET
    @Path(value = "/mdlist")
    public MetadataIndexDto getMetadataIndexList(@QueryParam(value = "scopeType") int scopeType,
        @DefaultValue("") @QueryParam(value = "text") String text,
        @QueryParam(value = "path") String path,
        @QueryParam(value = "metadataTypeList") String metadataTypeList,
        @DefaultValue("") @QueryParam(value = "selectedPackageSource") String selectedPackageSource,
        @DefaultValue("true") @QueryParam(value = "page") boolean page,
        @DefaultValue("10") @QueryParam(value = "pageSize") int pageSize,
        @DefaultValue("0") @QueryParam(value = "pageIndex") int pageIndex,
        @DefaultValue("false") @QueryParam(value = "conflictAvoid") boolean conflictAvoid) {
        if (metadataTypeList != null) {
            metadataTypeList = metadataTypeList.toLowerCase();
        }
        MetadataIndexDto dto = new MetadataIndexDto();
        List<MetadataIndexItemDto> list;
        switch (scopeType) {
            // 离线
            case 1:
                list = getLocalMetadataList(text, path, metadataTypeList, page, pageSize, pageIndex);
                break;
            // 远程
            case 2:
                list = getRemoteMetadataIndexList(text, path, selectedPackageSource, metadataTypeList, page, pageSize, pageIndex, false, conflictAvoid);
                break;
            // 引用
            case 3:
                list = getRemoteMetadataIndexList(text, path, selectedPackageSource, metadataTypeList, page, pageSize, pageIndex, true, conflictAvoid);
                break;
            default:
                throw new IllegalArgumentException("Param scope is invalid.");
        }
        if (list.size() > 0) {
            dto.setMetadataIndexItems(list);
            Page page1 = new Page();
            page1.setPageIndex(pageIndex);
            page1.setPageSize(list.size());
            dto.setPage(page1);
        } else {
            Page page2 = new Page();
            page2.setPageIndex(pageIndex);
            page2.setPageSize(0);
            dto.setPage(page2);
        }
        return dto;
    }

    /**
     * @param scopeType        查找范围0.默认 1.本地 2.已安装 3.已引用 4.仓库中
     * @param currentPath      当前路径
     * @param metadataIndexDto MetadataIndexItemDto实体
     * @param isNeedMdDto      是否需要？？？
     * @return com.inspur.edp.lcm.metadata.api.entity.Metadata4RefDto
     * @author zhongchq
     **/
    @POST
    @Path(value = "pickmd")
    public Metadata4RefDto pickMetadataIndex(@QueryParam(value = "scopeType") int scopeType,
        @QueryParam(value = "currentPath") String currentPath,
        MetadataIndexItemDto metadataIndexDto,
        @DefaultValue("true") @QueryParam(value = "isNeedMdDto") boolean isNeedMdDto) {
        return getMetadataService().pickMetadataIndex(scopeType, currentPath, metadataIndexDto, isNeedMdDto);
    }

    @GET
    @Path(value = "/metadata")
    public MetadataDto loadMetadataByMetadataId(String metadataId, String path) {
        final GspMetadata metadata = getMetadataService().loadMetadataByMetadataId(metadataId, path);
        Utils.checkNPE(metadata, "工程" + path + "中找不到元数据：" + metadataId);
        return MetadataDtoConverter.asDto(metadata);
    }

    @GET
    @Path(value = "/indexServerStatus")
    public String getIndexServerStatus(@QueryParam(value = "ip") String ip, @QueryParam(value = "port") String port) {
        return getMetadataService().getIndexServerStatus(ip, port);
    }

    @GET
    @Path(value = "/extendrule")
    public String getMdExtRule(@QueryParam(value = "metadataId") String metadataId,
        @QueryParam(value = "path") String path) {
        IMdExtRuleContent extendRule = getMetadataService().getMdExtRule(metadataId, path);
        String result = null;
        if (extendRule == null) {
            return null;
        }
        List<GspMetadata> metadataList = getMetadataService().getMetadataList(path);
        Optional<GspMetadata> optMetadata = metadataList.stream().filter(item -> item.getHeader().getId() == metadataId).findAny();
        if (!optMetadata.isPresent()) {
            throw new RuntimeException("不存在ID为" + metadataId + ",路径为" + path + "的元数据");
        }
        MdExtendRuleSerializer mdExtRulemanager = MdExtRuleSerializerHelper.getInstance().getManager(optMetadata.get().getHeader().getType());
        if (mdExtRulemanager != null) {
            JsonNode extRuleNode = mdExtRulemanager.serialize(extendRule);
            ObjectMapper objectMapper = Utils.getMapper();
            try {
                result = objectMapper.writeValueAsString(extRuleNode);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
        return result;
    }

    @PUT
    @Path(value = "/extendrule")
    public void saveMdExtRule(@QueryParam(value = "metadataId") String metadataId,
        @QueryParam(value = "path") String path, String extendRuleStr) {
        List<GspMetadata> metadataList = getMetadataService().getMetadataList(path);
        Optional<GspMetadata> optMetadata = metadataList.stream().filter(item -> item.getHeader().getId() == metadataId).findAny();
        if (!optMetadata.isPresent()) {
            throw new RuntimeException("不存在ID为" + metadataId + ",路径为" + path + "的元数据");
        }
        MdExtendRuleSerializer mdExtRulemanager = MdExtRuleSerializerHelper.getInstance().getManager(optMetadata.get().getHeader().getType());
        IMdExtRuleContent extendRule = null;
        if (mdExtRulemanager != null) {
            ObjectMapper objectMapper = Utils.getMapper();
            try {
                JsonNode extRuleNode = objectMapper.readTree(extendRuleStr);
                extendRule = mdExtRulemanager.deSerialize(extRuleNode);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        getMetadataService().saveMdExtRule(metadataId, path, extendRule);
    }

    @GET
    @Path(value = "/processMode")
    public String getProcessMode(@QueryParam(value = "path") String path) {
        return getMetadataProjectService().getProcessMode(path).toString();
    }

    @GET
    @Path(value = "/projinpath")
    public List<String> getProjPathsInPath(@QueryParam(value = "path") String path) {
        return getMetadataProjectService().getProjPathsInPath(path);
    }

    @GET
    @Path(value = "/isMetadataRefed")
    public boolean isMetadataRefed(@QueryParam(value = "path") String path) {
        return getMetadataService().isMetadataRefed(path);
    }

    @GET
    @Path(value = "/mdpkgNameExist")
    public String getMdpkgNameExistInBo(@QueryParam(value = "path") String path,
        @QueryParam(value = "mdpkgName") String mdpkgName) {
        return getMetadataProjectService().getMdpkgNameExistInBo(path, mdpkgName);
    }

    @POST
    @Path(value = "/changeProcessMode")
    public void changeProcessMode(@QueryParam(value = "path") String path,
        @QueryParam(value = "processMode") String processMode) {
        getMetadataProjectService().changeProcessMode(path, ProcessMode.valueOf(processMode));
    }

    private List<MetadataIndexItemDto> getRemoteMetadataIndexList(String text, String path,
        String selectedPackageSource,
        String metadataTypeList, boolean page, int pageSize,
        int pageIndex, boolean isFilterByRefs, boolean conflictAvoidFlag) {
        List<String> typeList = handleMetadataTypeList(metadataTypeList);
        return getMetadataService().getRemoteMetadataIndexList(text, path, selectedPackageSource, typeList, page,
            pageSize, pageIndex, isFilterByRefs, conflictAvoidFlag);
    }

    private List<MetadataIndexItemDto> getLocalMetadataList(String text, String path, String metadataTypeList,
        boolean page, int pageSize, int pageIndex) {
        List<String> typeList = handleMetadataTypeList(metadataTypeList);
        return getMetadataService().getLocalMetadataList(text, path, typeList, page, pageSize, pageIndex);
    }

    private List<MetadataDto> asMdDtos(List<GspMetadata> mds, ProcessMode processMode) {
        if (mds == null) {
            return null;
        }
        List<MetadataDto> list = new ArrayList<>();
        mds.forEach(md -> {
            MetadataDto metadataDto = MetadataDtoConverter.asDto(md);
            metadataDto.setProcessMode(processMode.toString());
            list.add(metadataDto);
        });
        return list;
    }

    private List<String> handleMetadataTypeList(String metadataTypes) {
        List<String> typeList = null;
        if (!(metadataTypes == null || metadataTypes.isEmpty())) {
            String[] types = metadataTypes.split(",");
            typeList = new ArrayList<>(Arrays.asList(types));
        }
        return typeList;
    }
}
